{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f11197eb",
   "metadata": {},
   "source": [
    "## 1.1 目标检测综述。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95b9554c",
   "metadata": {},
   "source": [
    "<img src=\"./picture/1.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba4b9833",
   "metadata": {},
   "source": [
    "上面的图片讲述了分类检测以及分割的定义：\n",
    "\n",
    "1.分类就是输入一张图片，然后输出他的类别\n",
    "\n",
    "2.目标检测就是将含有多个对象的图片用框给框出来并识别出他\n",
    "\n",
    "3.分割是不满足于基本的框，而是将他的像素点都给抠出来，就像上面的一样"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98c97ee9",
   "metadata": {},
   "source": [
    "## 1.2预测阶段。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "157985a7",
   "metadata": {},
   "source": [
    "在模型训练完之后，用于预测，就是预测阶段。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "211d6e33",
   "metadata": {},
   "outputs": [],
   "source": [
    "# <img src=\"./picture/2.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36a85cb6",
   "metadata": {},
   "source": [
    "<img src=\"./picture/2.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f7deba8",
   "metadata": {},
   "source": [
    "上述图片就是yolo V1的模型了，其输入是一个448 * 448 * 3 的一个图像，然后输出是一个7 * 7 * 30的一个矩阵，只要解析这个矩阵的信息就可以得到目标检测的坐标点了。\n",
    "\n",
    "这是一个单次一步到位的算法，所以速度比较快。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffe97e4b",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "b1c0434f",
   "metadata": {},
   "source": [
    "<img src=\"./picture/3.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e424253f",
   "metadata": {},
   "source": [
    "这里讲述了yolo算法的核心原理：\n",
    "\n",
    "1.首先将图像压缩成一个448 * 448 * 3 的一个张量，然后再使用7 * 7 * 3去划分，每一个7 * 7都是一个小格子。\n",
    "前面也学习到了吴恩达课程中的一个叫Bounding Boxes的东西(这个就表示一个格子可能包含不知一个类别)。每一个小格子都会\n",
    "画出两个Bounding Boxes。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25ec1de3",
   "metadata": {},
   "source": [
    "<img src=\"./picture/4.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dae0c9ac",
   "metadata": {},
   "source": [
    "2.在这里也讲述了最终的输出代表的是什么：\n",
    "\n",
    "首先这个7 * 7 表示的是前面划分的一个个小格子，然后这个30就包含了信息，就是小格子中两个框的信息，小格子是否有检测物体以及被检测物体的概率。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db97a983",
   "metadata": {},
   "source": [
    "## 1.3 预测阶段的后处理部分（置信度过滤以及非极大值抑制）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ddfd4702",
   "metadata": {},
   "source": [
    "### 1.3.1 置信度过滤"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a9a1c74",
   "metadata": {},
   "source": [
    "<img src=\"./picture/5.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa314160",
   "metadata": {},
   "source": [
    "在经历了yolo模型的黑箱之后，要对数据进行后处理，因为最终输出的7 * 7 * 30 确实包含了所有的预测信息，但是也有很多无用的信息，因为我们要的信息只是检测出那个对象即可。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "71352814",
   "metadata": {},
   "source": [
    "<img src=\"./picture/6.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e782ca5",
   "metadata": {},
   "source": [
    "1.这里有个关键的一点，前面讲述了这个Bounding Boxes 前5个向量表示的是置信水平和第一个Bounding boxes的信息，5 - 10是第二个框的信息\n",
    "\n",
    "2.然后要将每个Bounding boxes都乘以后面20个向量（后面20个向量表示的是要预测的类别的概率）前面的置信水平就是表示这个框内有检测物体的概率\n",
    "，这个置信水平乘以后面20个类别的概率表示的就是这里有这个类别的概率。\n",
    "\n",
    "3.最后可以得出98 * 20 个向量（这里49个格子，每个格子有两个Bounding boxes，20表示的是类别）\n",
    "然后就可以根据这些信息来得出最终这个小格子这个框到底有没有检测出物体。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e2a12bd",
   "metadata": {},
   "source": [
    "<img src=\"./picture/7.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75e943e6",
   "metadata": {},
   "source": [
    "这里讲述了怎么对每个类别进行操作：\n",
    "\n",
    "首先要设置一个阈值（这里设置的是0.2），设置玩之后，第一行是狗的类别，如果这一行中数字小于0.2，那么就将这些数字置零处理\n",
    "然后再将这一行的数据进行从大到小的排序。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c79c76c",
   "metadata": {},
   "source": [
    "### 1.3.2 非最大值抑制"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdba98b6",
   "metadata": {},
   "source": [
    "<img src=\"./picture/8.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c23ca3fe",
   "metadata": {},
   "source": [
    "在进行完上述的操作之后，就要对剩下的进行非极大值抑制。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "252d0955",
   "metadata": {},
   "source": [
    "<img src=\"./picture/9.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26bd2bb5",
   "metadata": {},
   "source": [
    "这里就是非极大值抑制的具体步骤了：\n",
    "\n",
    "1.首先上面将小于阈值的全部置零，然后将狗这个类别进行排序处理，接下来将最大的下标对应的框和后面的依次进行对比。\n",
    "\n",
    "2.这里还有一个叫做IoU的阈值，这个阈值这里设置的是0.5，如果第一个框和第二个框的IoU大于0.5，那么可以认为第一个框和第二个框识别到了同一个物体，所以要把概率低的那个给删除掉。\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07629c98",
   "metadata": {},
   "source": [
    "<img src=\"./picture/10.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e74b12ec",
   "metadata": {},
   "source": [
    "3.在这里可以看到，粉色和橙色的框的IoU小于0.5，也就是说这个框识别到了其他的东西或者在其他地方也有一条狗（因为一张图片有可能有很多的狗），然后这个框可以苟且的先活着。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ce5a59b",
   "metadata": {},
   "source": [
    "<img src=\"./picture/11.png\" alt=\"Drawing\" style=\"width: 600px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67292733",
   "metadata": {},
   "source": [
    "4.将所有的框都和第一个（最大的）比完之后，可以使用剩下的最大的和不为零的逐一进行比较，然后和上述的操作是一样的，IoU大于0.5的就要被干掉。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28d14f39",
   "metadata": {},
   "source": [
    "#### 这里要注意的一点就是，上述只是对狗的这一个类别进行对比，实际上要对每一个类别都这样进行对比，这个样子就可以检测出所有的类别了。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "feba3275",
   "metadata": {},
   "source": [
    "<img src=\"./picture/12.png\" alt=\"Drawing\" style=\"width: 600px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19dfca5a",
   "metadata": {},
   "source": [
    "经过上述的一系列的操作，最终可以输出这3个框：\n",
    "\n",
    "1.关于这三个框的怎么输出的，首先这里每一列的列名表示的是一个框，然后每一个框都有20个分量，每个分量表示的就是这个框是不是这个类别的概率\n",
    "\n",
    "2.然后找出每个框也就是每一列的最大值，只要这个框有最大值（其实很多框里面的数字都置零了），那么这个最大值代表的就是这个类别，同时也可以输出这个框和对应的类别。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b73a180",
   "metadata": {},
   "source": [
    "### 这里要特别注意，以上所将的都是预测阶段的过程，也就是说我已经将这个模型训练好了，然后随便将一张图片丢进去进行识别，而在训练阶段，是没有所谓的非极大值抑制以及置信度过滤的过程，在反向传播的过程中，是不能将这些数据置零的，因为会影响损失函数，这些看似“无关紧要”的值其实在反向传播中时时刻刻都在影响整个模型。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cc1d7c6",
   "metadata": {},
   "source": [
    "## 1.4 训练阶段（反向 传播）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "909ba21e",
   "metadata": {},
   "source": [
    "训练阶段也是最难最重点的地方。\n",
    "其实在深度学习以及监督学习中，是通过梯度下降以及每次迭代微小的改变参数的值来取得损失函数的局部最小值的。\n",
    "\n",
    "这里要强调一点就是之前看吴恩达的课程的时候，介绍卷积核的时候介绍了一个111,000,111的卷积核，其实当时就会非常的好奇，为什么会使用那么多同样的卷积核，卷积核都是一样的啊。\n",
    "\n",
    "这里关键的一点就是搞混了前向传播和反向传播！我的理解是前向传播的理解，其实在神经网络的训练 过程中，就是要不断的训练这些参数（也就是卷积核和全连接层的参数），也就是说，在实际的前向传播（也就是预测阶段），那些卷积核都是千奇百怪的。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "292efdb0",
   "metadata": {},
   "source": [
    "### 1.4.1 训练集中的人工标注信息"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9aaf7f6",
   "metadata": {},
   "source": [
    "<img src=\"./picture/13.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "156ad411",
   "metadata": {},
   "source": [
    "首先在拿到一个训练集后，会有一些表示框的数据，然后这个框肯定会有一个中心点落在了7 * 7 的一个格子内，然后再这里每一个格子有两个Bounding Boxes,任务就是找到这个中心点所属的格子最能拟合训练集的那个框。\n",
    "\n",
    "在这里还要说明一下为什么yoloV1版本预测小目标以及密集目标的时候效果很差，因为每一个格子只能检测出一个物体，在这里有49个格子，理论上面就可以预测出49个物体，在遇到密集的物体的时候就..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fabce4e4",
   "metadata": {},
   "source": [
    "### 1.4.2 yolo中是如何确定哪个框是最好的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28daea9a",
   "metadata": {},
   "source": [
    "<img src=\"./picture/14.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2b32a89",
   "metadata": {},
   "source": [
    "上面说到，人工标注有一个中心点，然后在反向传播的过程中，要找到最能拟合这个框的一个框（每个grid cell有两个框）\n",
    "\n",
    "这里说的简单一点就是要找到一个和人工标注的那个框和这个Bounding boxes的IoU中最大的那个（这里的使用和预测阶段是不一样的啊！）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aaf22684",
   "metadata": {},
   "source": [
    "<img src=\"./picture/15.png\" alt=\"Drawing\" style=\"width: 500px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a98569ec",
   "metadata": {},
   "source": [
    "假如在训练集中没有中心点落在这个grid cell里面的话，那这个grid cell的置信水平越小越好，最好是接近于零。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e6182f68",
   "metadata": {},
   "source": [
    "### 1.4.3 yolo V1 的损失函数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "76d449a5",
   "metadata": {},
   "source": [
    "<img src=\"./picture/16.png\" alt=\"Drawing\" style=\"width: 700px;\" align=\"left\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75a8d962",
   "metadata": {},
   "source": [
    "这个看起来好像很可怕，但是仔细一看其实都是一些均方误差（其实想来也对，框和框的对比其实使用一个均方误差就可以了）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06c26db2",
   "metadata": {},
   "source": [
    "这里由上到下分别对损失函数进行分析;\n",
    "\n",
    "1.第一个是中心点定位误差：其中中间那个很像1的那个可以看做是一个0 - 1 变量，其中i表示的是第i个grid cell ,j表示的是第几个Bounding boxes.\n",
    "可以看到第一个其实就是遍历每个grid cell.\n",
    "\n",
    "2.第二个是宽高定位的误差，这里也是一个均方误差，这里对宽高开根号了，可以使小框对相对误差更加敏感。这个样子可以平衡小框和大框，对于大框来说更加的公平吧。\n",
    "\n",
    "3.第三个是负责检测bounding Boxes的误差\n",
    "\n",
    "4. 第四个负责检测的是\n",
    "\n",
    "5.第五个负责检测的是类别预测误差，和softmax很像，就是对应类别相减的均方。\n",
    "\n",
    "6.这里还要提一点的是，前面不是有个$\\lamda$吗。这个$\\lamda$表示的是权重，由于上面的坐标回归误差的重要性比较高，所以可以给他们赋予一个比较高的权重，而第四个公式\n",
    "由于是被打入冷宫的框，所以显得不那么重要，这个时候可以给他们赋予一个比较小的权重。\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49e0c768",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53a485de",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39270f3f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
